package com.chaos.framework.model.helper;


import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 循环批量处理器  分页   如果要支持id 分页
 *
 * @param <T>
 */
public class BatchHelper<T> {


    @AllArgsConstructor
    @Getter
    public enum BatchType implements EnumHelper.EnumType {
        /**
         * 分页逻辑须自己实现
         */
        ID(1, "主键id分页"),
        PAGE(2, "页码分页"),
        ;
        private final int value;
        private final String name;

    }

    public static final String PARAMS_PRE_LAST_ID = "preLastId";
    public static final String PARAMS_FETCH_SIZE = "fetchSize";
    public static final String PARAMS_CURRENT_PAGE = "currentPage";


    /**
     * id 提取器: 获取下一次循环的 preLastId
     */
    private Function<T, Long> idExtractor;

    /**
     * 数据提取器: 用于分批提取数据
     */
    private Fetcher<T> fetcher;

    /**
     * 数据处理器: 用于处理单个数据
     */
    private Consumer<List<T>> handler;

    private BatchType batchType;
    // 可选参数
    private Map<String, Object> params;

    private BatchHelper(Function<T, Long> idExtractor, Fetcher<T> fetcher, Consumer<List<T>> handler, Map<String, Object> params,
                        BatchType batchType) {
        this.idExtractor = idExtractor;
        this.fetcher = fetcher;
        this.handler = handler;
        this.params = params;
        this.batchType = batchType;
    }


    @FunctionalInterface
    public interface Fetcher<T> {

        List<T> fetch(Map<String, Object> params);

    }

    public static <T> LoopHandlerBuilder<T> builder() {
        return new LoopHandlerBuilder<>();
    }

    public static class LoopHandlerBuilder<T> {
        private Function<T, Long> idExtractor;
        private Fetcher<T> fetcher;
        private Consumer<List<T>> handler;
        private Map<String, Object> params = new HashMap<>();
        private long preLastId;
        private int currentPage;
        private int batchSize;
        /**
         * 批处理类型 1 id分页  2页码分页
         */
        private BatchType batchType;

        public LoopHandlerBuilder<T> idExtractor(Function<T, Long> idExtractor) {
            this.idExtractor = idExtractor;
            return this;
        }

        public LoopHandlerBuilder<T> fetcher(Fetcher<T> fetcher) {
            this.fetcher = fetcher;
            return this;
        }

        public LoopHandlerBuilder<T> handler(Consumer<List<T>> handler) {
            this.handler = handler;
            return this;
        }

        public LoopHandlerBuilder<T> batchType(BatchType batchType) {
            this.batchType = batchType;
            return this;
        }

        public LoopHandlerBuilder<T> currentPage(int currentPage) {
            this.currentPage = currentPage;
            return this;
        }

        public LoopHandlerBuilder<T> preLastId(long preLastId) {
            this.preLastId = preLastId;
            return this;
        }

        public LoopHandlerBuilder<T> batchSize(int batchSize) {
            this.batchSize = batchSize;
            return this;
        }

        public LoopHandlerBuilder<T> addParam(String key, Object value) {
            checkParam(key);
            params.put(key, value);
            return this;
        }

        public LoopHandlerBuilder<T> addParams(Map<String, Object> params) {
            checkParam(params);
            this.params.putAll(params);
            return this;
        }

        public BatchHelper<T> build() {
            Objects.requireNonNull(batchType, "batchType 不能为 null");
            if (batchType.getValue() == BatchType.ID.getValue()) {
                Objects.requireNonNull(idExtractor, "idExtractor 不能为 null");
            }
            Objects.requireNonNull(fetcher, "fetcher 不能为 null");
            Objects.requireNonNull(handler, "handler 不能为 null");

            if (batchSize == 0) {
                batchSize = 200;
            }
            if (currentPage == 0) {
                currentPage = 1;
            }
            params.put(PARAMS_PRE_LAST_ID, preLastId);
            params.put(PARAMS_FETCH_SIZE, batchSize);
            params.put(PARAMS_CURRENT_PAGE, currentPage);
            return new BatchHelper<>(idExtractor, fetcher, handler, params, batchType);
        }

        private void checkParam(String key) {
            if (key.equals(PARAMS_PRE_LAST_ID)) {
                throw new IllegalArgumentException("参数中不能包含关键字: " + PARAMS_PRE_LAST_ID);
            }

            if (key.equals(PARAMS_FETCH_SIZE)) {
                throw new IllegalArgumentException("参数中不能包含关键字: " + PARAMS_FETCH_SIZE);
            }
        }

        private void checkParam(Map<String, Object> params) {
            if (Objects.nonNull(params.get(PARAMS_PRE_LAST_ID))) {
                throw new IllegalArgumentException("参数中不能包含关键字: " + PARAMS_PRE_LAST_ID);
            }

            if (Objects.nonNull(params.get(PARAMS_FETCH_SIZE))) {
                throw new IllegalArgumentException("参数中不能包含关键字: " + PARAMS_FETCH_SIZE);
            }
        }

    }

    public void handle() {
        int fetchSize = (int) params.get(PARAMS_FETCH_SIZE);
        while (true) {
            final List<T> result = fetcher.fetch(params);
            if (result == null || result.isEmpty()) {
                break;
            }
            if (this.batchType.getValue() == BatchType.PAGE.getValue()) {
                int current = (int) params.get(PARAMS_CURRENT_PAGE);
                current++;
                params.put(PARAMS_CURRENT_PAGE, current);
            }
            if (this.batchType.getValue() == BatchType.ID.getValue()) {
                long preLastId = idExtractor.apply(result.get(result.size() - 1));
                params.put(PARAMS_PRE_LAST_ID, preLastId);
            }
            boolean needNextLoop = result.size() >= fetchSize;
            handler.accept(result);
            if (!needNextLoop) {
                break;
            }
        }
    }


}
